/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.web.core.jswdk;
import java.io.IOException;
import java.io.File;
import java.beans.*;
import java.util.Map;
import java.util.HashMap;
import java.util.Properties;
import java.util.Enumeration;
import java.util.Iterator;
import java.text.MessageFormat;
import java.net.URL;
import java.net.MalformedURLException;
import org.openide.TopManager;
import org.openide.filesystems.*;
import org.openide.execution.NbProcessDescriptor;
import org.openide.util.HelpCtx;
import org.openide.util.MapFormat;
import org.openide.util.NbBundle;
import org.openide.util.TaskListener;
import org.openide.util.Task;
import org.openide.loaders.DataObject;
import org.openide.execution.ProcessExecutor;
import org.openide.execution.NbClassPath;
import org.openide.execution.ExecInfo;
import org.openide.execution.ExecutorTask;
import org.netbeans.modules.web.core.WebExecSupport;
/** Executes a class externally (in a separate process). Provides
* basic implementation that allows to specify the process to
* execute, its parameters and also to substitute the content of repositorypath,
* classpath, bootclasspath and librarypath. This is done by inner class Format.
* <P>
* The behaviour described here can be overriden by subclasses to use different
* format (extend the set of recognized tags), execute the
* process with additional environment properties, etc.
* <p>
* By default the server is not started each time an object is executed, a running instance is used and the
* object is reloaded to the server.
* However, the server is always restarted in these situations:
* <ul>
* <li>The user runs the object by "Execute (restart server)" action</li> // NOI18N
* <li>The object being run is a servlet</li>
* <li>The executor used for execution has changed since the last server start</li>
* <li>Any property of the executor has changed since the last server start</li>
* <li>The document root for the server is different from that of the running server instance</li>
* <li>When any JSP on the filesystem on which the instance is running is cleaned</li>
* </ul>
*
* @author Petr Jiricka, Ales Novak, Jaroslav Tulach
*/
public class ServletJspExecutor extends ProcessExecutor implements ServletParamsCookie {
/** A pig wrote this code : serves to distinguish between "normal" run action
* and "restart server and run" action. */ // NOI18N
private static boolean restartServerRunAction = false;
public static final String PROP_PORT = "port"; // NOI18N
public static final String PROP_DOCUMENTROOT = "documentRoot"; // NOI18N
public static final String PROP_MIMETYPES = "MIMETypes"; // NOI18N
public static final String PROP_WELCOMEFILES = "welcomeFiles"; // NOI18N
public static final String PROP_INVOKER = "invoker"; // NOI18N
/** DecRoot for the currently executing process. */
private transient FileObject docRoot;
private static ExecutorTask serverInstance;
private static FileObject serverInstanceDocumentRoot;
private static ServletJspExecutor serverInstanceExecutor;
private static PropertyChangeListener pcl;
public static void forceRestart() {
//System.out.println("forcing restart");
restartServerRunAction = true;
}
public static void killServerIfRunning() {
if (serverInstance != null) {
serverInstance.stop();
serverInstance.getInputOutput().closeInputOutput();
cleanServerInstance();
}
}
private static void cleanServerInstance() {
serverInstance = null;
serverInstanceDocumentRoot = null;
if ((serverInstanceExecutor != null) && (pcl != null))
serverInstanceExecutor.removePropertyChangeListener(pcl);
serverInstanceExecutor = null;
pcl = null;
}
private int port = 8080;
private FileSystem documentRoot;
private Map mimeTypes;
private String welcomeFiles = "index.jsp,index.html,index.htm"; // NOI18N
private boolean invoker = true;
static final long serialVersionUID =6058359069466864370L;
/** Create a new executor.
* The default Java launcher associated with this VM's installation will be used,
* and the user repository entries will be used for the class path.
*/
public ServletJspExecutor() {
super();
mimeTypes = EditServletParamsAction.getDefaultMimeMap();
//setExternalExecutor(DEFAULT_APPLET_DESCRIPTOR);
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
firePropertyChange(PROP_PORT, null, new Integer(port));
}
public FileSystem getDocumentRoot() {
return documentRoot;
}
public void setDocumentRoot(FileSystem documentRoot) {
this.documentRoot = documentRoot;
firePropertyChange(PROP_DOCUMENTROOT, null, documentRoot);
}
public Map getMIMETypes() {
return mimeTypes;
}
public void setMIMETypes(Map mimeTypes) {
this.mimeTypes = mimeTypes;
firePropertyChange(PROP_MIMETYPES, null, mimeTypes);
}
public String getWelcomeFiles() {
return welcomeFiles;
}
public void setWelcomeFiles(String welcomeFiles) {
this.welcomeFiles = welcomeFiles;
firePropertyChange(PROP_WELCOMEFILES, null, welcomeFiles);
}
public boolean isInvoker() {
return invoker;
}
public void setInvoker(boolean invoker) {
this.invoker = invoker;
firePropertyChange(PROP_INVOKER, null, new Boolean(invoker));
}
/* Default human-presentable name of the executor.
* In the default implementation, just the class name.
* @return initial value of the human-presentable name
*/
public String displayName() {
return NbBundle.getBundle(ServletJspExecutor.class).getString("CTL_Exec_Name");
}
public HelpCtx getHelpCtx () {
return new HelpCtx (ServletJspExecutor.class);
}
/** Called to create the java.lang.Process for given exec info.
* Current implementation scans creates new Format with provided
* exec info and asks the current executor to start with that
* format.
* <P>
* Subclasses can override this to achive the right behaviour, add
* system properties, own format, etc.
*
* @param info exec info
* @return the executed process
* @exception IOException if the action fails
*/
protected Process createProcess (ExecInfo info) throws IOException {
return getExternalExecutor ().exec (
new Format(new ExecInfo(ServletMain.class.getName(),
new String[] {"" + getPort(), NbClassPath.toFile(docRoot).toURL().toString(), // NOI18N
EditServletParamsAction.getWorkDir(docRoot)}))
//new ServletJspFormat (getPort(), NbClassPath.toFile(docRoot).toURL().toString(), getWorkDir())
);
}
/* Executes given class by creating new process in underlying operating system.
* @param ctx used to write to the Output Window
* @param info information about the class to be executed
*/
public ExecutorTask execute(ExecInfo info) throws IOException {
try {
// find out the docRoot
docRoot = EditServletParamsAction.resolveDocRoot(info, getDocumentRoot());
// force restart if docRoot differs
if (docRoot != serverInstanceDocumentRoot)
forceRestart();
// force restart if this is a servlet
if (EditServletParamsAction.isServlet(info))
forceRestart();
// force restart if the executor has changed
if (serverInstanceExecutor != this)
forceRestart();
// construct the URL for the browser
final URL url =
(EditServletParamsAction.isServlet(info)) ?
EditServletParamsAction.constructServletURL(EditServletParamsAction.findDataObject(info), getPort(), isInvoker()) :
EditServletParamsAction.constructJspURL(EditServletParamsAction.findDataObject(info), getPort());
// stop the previously executed instance
if (restartServerRunAction) {
killServerIfRunning();
}
// start the web browser
Thread browser = new Thread(new Runnable() {
public void run() {
if (!WebExecSupport.waitAndShowInBrowser(url, 10000)) {
// consider server not running
cleanServerInstance();
}
}
});
browser.start();
//System.out.println("(re)starting server " + (serverInstance == null));
if (serverInstance == null) {
// generate the properties
EditServletParamsAction.deployWebAppDescriptor(
docRoot, getMIMETypes(), getWelcomeFiles(), isInvoker());
// execute the process
serverInstance = super.execute(new ExecInfo(
java.text.MessageFormat.format(NbBundle.getBundle(ServletJspExecutor.class).
getString("CTL_Servlet_Process"), new Object[] {new Integer(getPort())})
));
serverInstanceDocumentRoot = docRoot;
serverInstanceExecutor = this;
pcl = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
forceRestart();
}
};
serverInstanceExecutor.addPropertyChangeListener(pcl);
serverInstance.addTaskListener(new TaskListener() {
public void taskFinished(Task task){
if (task == serverInstance)
cleanServerInstance();
}
});
}
return serverInstance;
}
catch (IOException e) {
throw e;
}
catch (Throwable e) {
throw new IOException();
}
finally {
restartServerRunAction = false;
}
}
}
/*
* Log
* 21 Gandalf 1.20 2/4/00 Petr Jiricka Restart the engine if
* the execution parameters change - fixes bugs 5561, 5515, 5581, 5291,
* 5587
* 20 Gandalf 1.19 1/18/00 Petr Jiricka Bugfix 2941
* 19 Gandalf 1.18 1/17/00 Petr Jiricka Debug outputs removed
* 18 Gandalf 1.17 1/16/00 Petr Jiricka Cleanup
* 17 Gandalf 1.16 1/12/00 Petr Jiricka i18n phase 1
* 16 Gandalf 1.15 1/4/00 Petr Jiricka More safe handling of
* running server process
* 15 Gandalf 1.14 1/3/00 Petr Jiricka Changed RootFileObject
* to RootFileSystem
* 14 Gandalf 1.13 12/29/99 Petr Jiricka Various execution fixes
* 13 Gandalf 1.12 12/21/99 Petr Jiricka Type of DocumentRoot
* property changed to FileSystem
* 12 Gandalf 1.11 12/20/99 Petr Jiricka Checking in changes made
* in the U.S.
* 11 Gandalf 1.10 11/27/99 Patrik Knakal
* 10 Gandalf 1.9 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 9 Gandalf 1.8 10/12/99 Petr Jiricka Removed debug messages
* 8 Gandalf 1.7 10/9/99 Petr Jiricka Cleanup
* 7 Gandalf 1.6 10/8/99 Petr Jiricka Various fixes
* 6 Gandalf 1.5 10/8/99 Petr Jiricka Show in browser in a
* separate thread
* 5 Gandalf 1.4 10/8/99 Petr Jiricka Showing in the web
* browser
* 4 Gandalf 1.3 10/7/99 Petr Jiricka
* 3 Gandalf 1.2 10/7/99 Petr Jiricka
* 2 Gandalf 1.1 10/7/99 Petr Jiricka
* 1 Gandalf 1.0 10/7/99 Petr Jiricka
* $
*/